/* -*- related-file-name: "../include/lcdf/clp.h" -*- */
/* clp.c - Complete source code for CLP.
 * This file is part of CLP, the command line parser package.
 *
 * Copyright (c) 1997-2006 Eddie Kohler, kohler@cs.ucla.edu
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file, which is available in full at
 * http://www.pdos.lcs.mit.edu/click/license.html. The conditions include: you
 * must preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding. */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <lcdf/clp.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#include <ctype.h>

/* By default, assume we have strtoul. */
#if !defined(HAVE_STRTOUL) && !defined(HAVE_CONFIG_H)
# define HAVE_STRTOUL 1
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* Option types for Clp_SetOptionChar */
#define Clp_DoubledLong		(Clp_LongImplicit * 2)

#define Clp_AnyArgument		(Clp_Mandatory | Clp_Optional)

#define MAX_AMBIGUOUS_VALUES	4

typedef struct {
    Clp_ArgParseFunc func;
    int flags;
    void *thunk;
} Clp_ArgType;


typedef struct {
    int pos;
    int neg;
} Clp_LongMinMatch;


struct Clp_Internal {

    Clp_Option *opt;
    Clp_LongMinMatch *long_min_match;
    int nopt;
  
    Clp_ArgType *argtype;
    int nargtype;
  
    const char * const *argv;
    int argc;
  
    unsigned char option_class[256];
    int both_short_and_long;
  
    char option_chars[3];
    const char *text;
  
    const char *program_name;
    void (*error_handler)(const char *);
  
    int is_short;
    int whole_negated;		/* true if negated by an option character */
    int could_be_short;
  
    int option_processing;
  
    int ambiguous;
    int ambiguous_values[MAX_AMBIGUOUS_VALUES];
  
    Clp_Option *current_option;
    int current_short;
    int negated_by_no;

};


struct Clp_ParserState {
    const char * const *argv;
    int argc;
    char option_chars[3];
    const char *text;
    int is_short;
    int whole_negated;
};


typedef struct Clp_StringList {
    Clp_Option *items;
    Clp_LongMinMatch *long_min_match;
    int nitems;
    int allow_int;
    int nitems_invalid_report;
} Clp_StringList;


#define TEST(o, f)		(((o)->flags & (f)) != 0)


static int calculate_long_min_match(int, Clp_Option *, int, int, int);

static int parse_string(Clp_Parser *, const char *, int, void *);
static int parse_int(Clp_Parser *, const char *, int, void *);
static int parse_bool(Clp_Parser *, const char *, int, void *);
static int parse_double(Clp_Parser *, const char *, int, void *);
static int parse_string_list(Clp_Parser *, const char *, int, void *);

static int ambiguity_error(Clp_Parser *, int, int *, Clp_Option *,
			   const char *, const char *, ...);


/*******
 * Clp_NewParser, etc.
 **/

static void
check_duplicated_short_options(Clp_Parser *clp, int nopt, Clp_Option *opt, int negated)
{
    int i;
    int check[256];
  
    for (i = 0; i < 256; i++)
	check[i] = -1;
  
    for (i = 0; i < nopt; i++)
	if (opt[i].short_name > 0 && opt[i].short_name < 256
	    && (negated ? TEST(&opt[i], Clp_Negate)
		: !TEST(&opt[i], Clp_OnlyNegated))) {
	    int sh = opt[i].short_name;
	    if (check[sh] >= 0 && check[sh] != opt[i].option_id)
		Clp_OptionError(clp, "CLP internal error: more than 1 option has short name '%c'", sh);
	    check[sh] = opt[i].option_id;
	}
}

Clp_Parser *
Clp_NewParser(int argc, const char * const *argv, int nopt, Clp_Option *opt)
     /* Creates and returns a new Clp_Parser using the options in 'opt',
	or 0 on memory allocation failure */
{
    int i;
    Clp_Parser *clp = (Clp_Parser *)malloc(sizeof(Clp_Parser));
    Clp_Internal *cli = (Clp_Internal *)malloc(sizeof(Clp_Internal));
    Clp_LongMinMatch *lmm = (Clp_LongMinMatch *)malloc(sizeof(Clp_LongMinMatch) * nopt);
    if (!clp || !cli || !lmm)
	goto failed;
  
    clp->internal = cli;
    cli->long_min_match = lmm;

    /* Assign arguments (need to do this now so we can call Clp_OptionError) */
    cli->argc = argc;
    cli->argv = argv;
    {
	const char *slash = strrchr(argv[0], '/');
	cli->program_name = slash ? slash + 1 : argv[0];
    }
    cli->error_handler = 0;
  
    /* Get rid of negative option_ids, which are internal to CLP */
    for (i = 0; i < nopt; i++)
	if (opt[i].option_id < 0) {
	    Clp_OptionError(clp, "CLP internal error: option %d has negative option_id", i);
	    opt[i] = opt[nopt - 1];
	    nopt--;
	    i--;
	}
  
    /* Massage the options to make them usable */
    for (i = 0; i < nopt; i++) {
	/* Enforce invariants */
	if (opt[i].arg_type <= 0)
	    opt[i].flags &= ~Clp_AnyArgument;
	if (opt[i].arg_type > 0 && !TEST(&opt[i], Clp_Optional))
	    opt[i].flags |= Clp_Mandatory;
	
	/* Nonexistent short options have character 256. We know this won't
	   equal any character in an argument, even if characters are signed */
	if (opt[i].short_name <= 0 || opt[i].short_name > 255)
	    opt[i].short_name = 256;
    
	/* Options that start with 'no-' should be changed to OnlyNegated */
	if (opt[i].long_name && strncmp(opt[i].long_name, "no-", 3) == 0) {
	    opt[i].long_name += 3;
	    opt[i].flags |= Clp_Negate | Clp_OnlyNegated;
	}
    }
  
    /* Check for duplicated short options */
    check_duplicated_short_options(clp, nopt, opt, 0);
    check_duplicated_short_options(clp, nopt, opt, 1);
  
    /* Calculate long options' minimum unambiguous length */
    for (i = 0; i < nopt; i++)
	if (opt[i].long_name && !TEST(&opt[i], Clp_OnlyNegated))
	    lmm[i].pos = calculate_long_min_match
		(nopt, opt, i, Clp_OnlyNegated, 0);
    for (i = 0; i < nopt; i++)
	if (opt[i].long_name && TEST(&opt[i], Clp_Negate))
	    lmm[i].neg = calculate_long_min_match
		(nopt, opt, i, Clp_Negate, Clp_Negate);
  
    /* Set up clp->internal */
    cli->opt = opt;
    cli->nopt = nopt;
  
    cli->argtype = (Clp_ArgType *)malloc(sizeof(Clp_ArgType) * 8);
    if (!cli->argtype)
	goto failed;
    for (i = 0; i < 8; i++)
	cli->argtype[i].func = 0;
    cli->nargtype = 8;
  
    for (i = 0; i < 256; i++)
	cli->option_class[i] = 0;
    cli->option_class[(unsigned char) '-'] = Clp_Short;
    cli->both_short_and_long = 0;
    
    cli->is_short = 0;
    cli->whole_negated = 0;
  
    cli->option_processing = 1;
    cli->current_option = 0;

    /* Add default type parsers */
    Clp_AddType(clp, Clp_ArgString, 0, parse_string, 0);
    Clp_AddType(clp, Clp_ArgStringNotOption, Clp_DisallowOptions, parse_string, 0);
    Clp_AddType(clp, Clp_ArgInt, 0, parse_int, 0);
    Clp_AddType(clp, Clp_ArgUnsigned, 0, parse_int, (void *)cli);
    Clp_AddType(clp, Clp_ArgBool, 0, parse_bool, 0);
    Clp_AddType(clp, Clp_ArgDouble, 0, parse_double, 0);
    return clp;
    
  failed:
    /* It should be OK to free(0), but on some ancient systems, it isn't */
    if (cli && cli->argtype)
	free(cli->argtype);
    if (cli)
	free(cli);
    if (clp)
	free(clp);
    if (lmm)
	free(lmm);
    return 0;
}

void
Clp_DeleteParser(Clp_Parser *clp)
     /* Destroys the Clp_Parser 'clp' and any associated memory allocated by
        CLP */
{
    int i;
    Clp_Internal *cli;
    if (!clp)
	return;
  
    cli = clp->internal;
  
    /* get rid of any string list types */
    for (i = 0; i < cli->nargtype; i++)
	if (cli->argtype[i].func == parse_string_list) {
	    Clp_StringList *clsl = (Clp_StringList *)cli->argtype[i].thunk;
	    free(clsl->items);
	    free(clsl->long_min_match);
	    free(clsl);
	}

    free(cli->argtype);
    free(cli->long_min_match);
    free(cli);
    free(clp);
}

int
Clp_SetOptionProcessing(Clp_Parser *clp, int option_processing)
     /* Sets whether command line arguments are parsed (looking for options)
	at all. Each parser starts out with OptionProcessing true. Returns old
	value. */
{
    Clp_Internal *cli = clp->internal;
    int old = cli->option_processing;
    cli->option_processing = option_processing;
    return old;
}

Clp_ErrorHandler
Clp_SetErrorHandler(Clp_Parser *clp, void (*error_handler)(const char *))
     /* Sets a hook function to be called before Clp_OptionError
	prints anything. 0 means nothing will be called. */
{
    Clp_Internal *cli = clp->internal;
    Clp_ErrorHandler old = cli->error_handler;
    cli->error_handler = error_handler;
    return old;
}

int
Clp_SetOptionChar(Clp_Parser *clp, int c, int option_type)
     /* Determines how clp will deal with short options.
	option_type must be a sum of:

	0 == Clp_NotOption 'c' isn't an option.
	Clp_Short	'c' introduces a list of short options.
	Clp_Long	'c' introduces a long option.
	Clp_ShortNegated 'c' introduces a negated list of
			short options.
	Clp_LongNegated 'c' introduces a negated long option.
	Clp_LongImplicit 'c' introduces a long option, and *is part
			of the long option itself*.

	Some values are not allowed (Clp_Long | Clp_LongNegated isn't allowed,
	for instance). c=0 means ALL characters are that type. Returns 0 on
	failure (you gave an illegal option_type), 1 on success. */
{
    int i;
    Clp_Internal *cli = clp->internal;
  
    if (option_type < 0
	|| option_type >= 2*Clp_LongImplicit
	|| ((option_type & Clp_Short) && (option_type & Clp_ShortNegated))
	|| ((option_type & Clp_Long) && (option_type & Clp_LongNegated))
	|| ((option_type & Clp_LongImplicit) && (option_type & (Clp_Short | Clp_ShortNegated | Clp_Long | Clp_LongNegated))))
	return 0;
  
    if (c == 0)
	for (i = 1; i < 256; i++)
	    cli->option_class[i] = option_type;
    else
	cli->option_class[(unsigned char) c] = option_type;

    /* If an option character can introduce either short or long options, then
       we need to fix up the long_min_match values. We may have set the
       long_min_match for option 'abcde' to 1, if no other option starts
       with 'a'. But if '-' can introduce either a short option or a long
       option, AND a short option '-a' exists, then the long_min_match for
       'abcde' must be set to 2! */
    if (!cli->both_short_and_long) {
	int either_short = option_type & (Clp_Short | Clp_ShortNegated);
	int either_long = option_type & (Clp_Long | Clp_LongNegated);
	if (either_short && either_long) {
	    unsigned char have_short[257];
	    for (i = 0; i < 256; i++)
		have_short[i] = 0;
	    for (i = 0; i < cli->nopt; i++)
		have_short[cli->opt[i].short_name] = 1;
	    for (i = 0; i < cli->nopt; i++)
		if (cli->opt[i].long_name && cli->long_min_match[i].pos == 1) {
		    /* if '--Cxxxx's short name is '-C', keep long_min_match
		       == 1 */
		    unsigned char first = (unsigned char)cli->opt[i].long_name[0];
		    if (have_short[first] && first != cli->opt[i].short_name)
			cli->long_min_match[i].pos++;
		}
	    cli->both_short_and_long = 1;
	}
    }
  
    return 1;
}

const char *
Clp_ProgramName(Clp_Parser *clp)
{
    return clp->internal->program_name;
}

void
Clp_SetProgramName(Clp_Parser *clp, const char *program_name)
{
    clp->internal->program_name = program_name;
}


/*******
 * functions for Clp_Option lists
 **/

static int
min_different_chars(const char *s, const char *t)
     /* Returns the minimum number of characters required to distinguish
	s from t.
	If s is shorter than t, returns strlen(s). */
{
    const char *sfirst = s;
    while (*s && *t && *s == *t)
	s++, t++;
    if (!*s)
	return s - sfirst;
    else
	return s - sfirst + 1;
}

static int
calculate_long_min_match(int nopt, Clp_Option *opt, int which,
			 int flags, int flags_value)
{
    int j, lmm = 1;
    int preferred = (opt[which].flags & Clp_PreferredMatch);
  
    for (j = 0; j < nopt; j++)
	if (opt[j].long_name
	    && (opt[j].flags & flags) == flags_value
	    && opt[which].option_id != opt[j].option_id
	    && strncmp(opt[which].long_name, opt[j].long_name, lmm) == 0
	    && (!preferred || strncmp(opt[which].long_name, opt[j].long_name, strlen(opt[which].long_name)) != 0))
	    lmm = min_different_chars(opt[which].long_name, opt[j].long_name);
  
    return lmm;
}

/* the ever-glorious argcmp */

static int
argcmp(const char *ref, const char *arg, int min_match, int fewer_dashes)
     /* Returns 0 if ref and arg don't match.
	Returns -1 if ref and arg match, but fewer than min_match characters.
	Returns len if ref and arg match min_match or more characters;
	len is the number of characters that matched in arg.
	Allows arg to contain fewer dashes than ref iff fewer_dashes != 0.
	
	Examples:
	argcmp("x", "y", 1, 0)	-->  0	/ just plain wrong
	argcmp("a", "ax", 1, 0)	-->  0	/ ...even though min_match == 1
					and the 1st chars match
	argcmp("box", "bo", 3, 0) --> -1	/ ambiguous
	argcmp("cat", "c=3", 1, 0) -->  1	/ handles = arguments
	*/
{
    const char *refstart = ref;
    const char *argstart = arg;
    assert(min_match > 0);
    
  compare:
    while (*ref && *arg && *arg != '=' && *ref == *arg)
	ref++, arg++;

    /* Allow arg to contain fewer dashes than ref */
    if (fewer_dashes && *ref == '-' && ref[1] && ref[1] == *arg) {
	ref++;
	goto compare;
    }
    
    if (*arg && *arg != '=')
	return 0;
    else if (ref - refstart < min_match)
	return -1;
    else
	return arg - argstart;
}

static int
find_prefix_opt(const char *arg, int nopt, Clp_Option *opt,
		Clp_LongMinMatch *lmmvec,
		int *ambiguous, int *ambiguous_values, int negated)
     /* Looks for an unambiguous match of 'arg' against one of the long
        options in 'opt'. Returns positive if it finds one; otherwise, returns
        -1 and possibly changes 'ambiguous' and 'ambiguous_values' to keep
        track of at most MAX_AMBIGUOUS_VALUES possibilities. */
{
    int i, fewer_dashes = 0, first_ambiguous = *ambiguous;

  retry:
    for (i = 0; i < nopt; i++) {
	int len, lmm;
	if (!opt[i].long_name
	    || (negated && !TEST(&opt[i], Clp_Negate))
	    || (!negated && TEST(&opt[i], Clp_OnlyNegated)))
	    continue;
    
	lmm = (negated ? lmmvec[i].neg : lmmvec[i].pos);
	len = argcmp(opt[i].long_name, arg, lmm, fewer_dashes);
	if (len > 0)
	    return i;
	else if (len < 0) {
	    if (*ambiguous < MAX_AMBIGUOUS_VALUES)
		ambiguous_values[*ambiguous] = i;
	    (*ambiguous)++;
	}
    }

    /* If there were no partial matches, try again with fewer_dashes true */
    if (*ambiguous == first_ambiguous && !fewer_dashes) {
	fewer_dashes = 1;
	goto retry;
    }
    
    return -1;
}


/*****
 * Argument parsing
 **/

int
Clp_AddType(Clp_Parser *clp, int type_id, int flags,
	    Clp_ArgParseFunc func, void *thunk)
     /* Add a argument parser for type_id to the Clp_Parser. When an argument
	arg for Clp_Option opt is being processed, the parser routines will
	call (*func)(clp, opt, arg, thunk, complain)
	where complain is 1 if the routine should report errors, or 0 if it
	should fail silently.
	Returns 1 on success, 0 if memory allocation fails or the arguments
	are bad. */
{
    int i;
    Clp_Internal *cli = clp->internal;
    Clp_ArgType *new_argtype;
    int nargtype = cli->nargtype;
    assert(nargtype);
  
    if (type_id <= 0 || !func)
	return 0;
  
    while (nargtype <= type_id)
	nargtype *= 2;
    new_argtype = (Clp_ArgType *)
	realloc(cli->argtype, sizeof(Clp_ArgType) * nargtype);
    if (!new_argtype)
	return 0;
    cli->argtype = new_argtype;
  
    for (i = cli->nargtype; i < nargtype; i++)
	cli->argtype[i].func = 0;
    cli->nargtype = nargtype;
  
    cli->argtype[type_id].func = func;
    cli->argtype[type_id].flags = flags;
    cli->argtype[type_id].thunk = thunk;
    return 1;
}


/*******
 * Default argument parsers
 **/

static int
parse_string(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
    (void)complain, (void)thunk;
    clp->val.s = arg;
    return 1;
}

static int
parse_int(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
    char *val;
    int base = 10;
    if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) {
	base = 16;
	arg += 2;
    }
  
    if (thunk != 0) {		/* unsigned */
#if HAVE_STRTOUL
	clp->val.u = strtoul(arg, &val, base);
#else
	/* don't bother really trying to do it right */
	if (arg[0] == '-')
	    val = (char *) arg;
	else
	    clp->val.u = strtol(arg, &val, base);
#endif
    } else
	clp->val.i = strtol(arg, &val, base);
    if (*arg != 0 && *val == 0)
	return 1;
    else if (complain) {
	const char *message = thunk != 0
	    ? "'%O' expects a nonnegative integer, not '%s'"
	    : "'%O' expects an integer, not '%s'";
	if (base == 16) arg -= 2;
	return Clp_OptionError(clp, message, arg);
    } else
	return 0;
}

static int
parse_double(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
    char *val;
    (void)thunk;
    clp->val.d = strtod(arg, &val);
    if (*arg != 0 && *val == 0)
	return 1;
    else if (complain)
	return Clp_OptionError(clp, "'%O' expects a real number, not '%s'", arg);
    else
	return 0;
}

static int
parse_bool(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
    int i;
    char lcarg[6];
    (void)thunk;
    if (strlen(arg) > 5 || strchr(arg, '=') != 0)
	goto error;
  
    for (i = 0; arg[i] != 0; i++)
	lcarg[i] = tolower(arg[i]);
    lcarg[i] = 0;
  
    if (argcmp("yes", lcarg, 1, 0) > 0
	|| argcmp("true", lcarg, 1, 0) > 0
	|| argcmp("1", lcarg, 1, 0) > 0) {
	clp->val.i = 1;
	return 1;
    } else if (argcmp("no", lcarg, 1, 0) > 0
	       || argcmp("false", lcarg, 1, 0) > 0
	       || argcmp("1", lcarg, 1, 0) > 0) {
	clp->val.i = 0;
	return 1;
    }
  
  error:
    if (complain)
	Clp_OptionError(clp, "'%O' expects a true-or-false value, not '%s'", arg);
    return 0;
}


/*****
 * Clp_AddStringListType
 **/

static int
parse_string_list(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
    Clp_StringList *sl = (Clp_StringList *)thunk;
    int idx, ambiguous = 0;
    int ambiguous_values[MAX_AMBIGUOUS_VALUES + 1];
  
    /* actually look for a string value */
    idx = find_prefix_opt
	(arg, sl->nitems, sl->items, sl->long_min_match,
	 &ambiguous, ambiguous_values, 0);
    if (idx >= 0) {
	clp->val.i = sl->items[idx].option_id;
	return 1;
    }
  
    if (sl->allow_int) {
	if (parse_int(clp, arg, 0, 0))
	    return 1;
    }
  
    if (complain) {
	const char *complaint = (ambiguous ? "an ambiguous" : "not a valid");
	if (!ambiguous) {
	    ambiguous = sl->nitems_invalid_report;
	    for (idx = 0; idx < ambiguous; idx++)
		ambiguous_values[idx] = idx;
	}
	return ambiguity_error
	    (clp, ambiguous, ambiguous_values, sl->items, "",
	     "'%s' is %s argument to '%O'", arg, complaint);
    } else
	return 0;
}


int
finish_string_list(Clp_Parser *clp, int type_id, int flags,
		   Clp_Option *items, int nitems, int itemscap)
{
    int i;
    Clp_StringList *clsl = (Clp_StringList *)malloc(sizeof(Clp_StringList));
    Clp_LongMinMatch *lmm = (Clp_LongMinMatch *)malloc(sizeof(Clp_LongMinMatch) * nitems);
    if (!clsl || !lmm)
	goto error;
  
    clsl->items = items;
    clsl->long_min_match = lmm;
    clsl->nitems = nitems;
    clsl->allow_int = (flags & Clp_AllowNumbers) != 0;
  
    if (nitems < MAX_AMBIGUOUS_VALUES && nitems < itemscap && clsl->allow_int) {
	items[nitems].long_name = "any integer";
	clsl->nitems_invalid_report = nitems + 1;
    } else if (nitems > MAX_AMBIGUOUS_VALUES + 1)
	clsl->nitems_invalid_report = MAX_AMBIGUOUS_VALUES + 1;
    else
	clsl->nitems_invalid_report = nitems;
  
    for (i = 0; i < nitems; i++)
	lmm[i].pos = calculate_long_min_match(nitems, items, i, 0, 0);
    
    if (Clp_AddType(clp, type_id, 0, parse_string_list, clsl))
	return 1;

  error:
    if (clsl)
	free(clsl);
    if (lmm)
	free(lmm);
    return 0;
}

int
Clp_AddStringListType(Clp_Parser *clp, int type_id, int flags, ...)
     /* An easy way to add new types to clp.
	Call like this:
	Clp_AddStringListType
	  (clp, type_id, flags,
	   char *s_1, int value_1, ..., char *s_n, int value_n, 0);

        Defines type_id as a type in clp.
	This type accepts any of the strings s_1, ..., s_n
	(or unambiguous abbreviations thereof);
	if argument s_i is given, value_i is stored in clp->val.i.
	If Clp_AllowNumbers is set in flags,
	explicit integers are also allowed.

	Returns 1 on success, 0 on memory allocation errors. */
{
    int nitems = 0;
    int itemscap = 5;
    Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
  
    va_list val;
    va_start(val, flags);
  
    if (!items)
	goto error;
  
    /* slurp up the arguments */
    while (1) {
	int value;
	char *name = va_arg(val, char *);
	if (!name)
	    break;
	value = va_arg(val, int);
    
	if (nitems >= itemscap) {
	    Clp_Option *new_items;
	    itemscap *= 2;
	    new_items = (Clp_Option *)realloc(items, sizeof(Clp_Option) * itemscap);
	    if (!new_items)
		goto error;
	    items = new_items;
	}
    
	items[nitems].long_name = name;
	items[nitems].option_id = value;
	items[nitems].flags = 0;
	nitems++;
    }

    va_end(val);
    if (finish_string_list(clp, type_id, flags, items, nitems, itemscap))
	return 1;
  
  error:
    va_end(val);
    if (items)
	free(items);
    return 0;
}


int
Clp_AddStringListTypeVec(Clp_Parser *clp, int type_id, int flags,
			 int nitems, char **strings, int *values)
     /* An alternate way to make a string list type. See Clp_AddStringListType
	for the basics; this coalesces the strings and values into two arrays,
	rather than spreading them out into a variable argument list. */
{
    int i;
    int itemscap = (nitems < 5 ? 5 : nitems);
    Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
    if (!items)
	return 0;
  
    /* copy over items */
    for (i = 0; i < nitems; i++) {
	items[i].long_name = strings[i];
	items[i].option_id = values[i];
	items[i].flags = 0;
    }
  
    if (finish_string_list(clp, type_id, flags, items, nitems, itemscap))
	return 1;
    else {
	free(items);
	return 0;
    }
}


/*******
 * Returning information
 **/

Clp_Argv *
Clp_NewArgv(const char *str_in, int len)
{
    int argcap = 32;
    Clp_Argv *clp_argv = (Clp_Argv *)malloc(sizeof(Clp_Argv));
    char *s, *end;

    if (!clp_argv)
	goto failed_all;
    if (len < 0)
	len = strlen(str_in);
    if (!(clp_argv->argv_buf = (char *)malloc(len + 1)))
	goto failed_argv_buf;
    if (!(clp_argv->argv = (char **)malloc(sizeof(char *) * argcap)))
	goto failed_argv;
    memcpy(clp_argv->argv_buf, str_in, len);
    end = clp_argv->argv_buf + len;
    *end = 0;

    /* Parse arguments */
    s = clp_argv->argv_buf;
    clp_argv->argc = 0;
    while (1) {
	char *arg, *sout;
	int state;
	while (s < end && isspace((unsigned char) *s))
	    s++;
	if (s >= end)
	    break;
	/* Parse single argument */
	for (arg = sout = s, state = 0; s < end; s++) {
	    if (*s == '\'') {
		if (state == 0)
		    state = '\'';
		else if (state == '\'')
		    state = 0;
		else
		    *sout++ = *s;
	    } else if (*s == '\"') {
		if (state == 0)
		    state = '\"';
		else if (state == '\"')
		    state = 0;
		else
		    *sout++ = *s;
	    } else if (*s == '\\') {
		if (state == '\'' || s == end - 1)
		    *sout++ = *s;
		else
		    *sout++ = *++s;
	    } else if (isspace((unsigned char) *s)) {
		if (state == 0)
		    break;
		else
		    *sout++ = *s;
	    } else
		*sout++ = *s;
	}
	*sout = 0;
	s++;
	/* Store argument */
	if (clp_argv->argc < argcap - 2) {
	    argcap *= 2;
	    if (!(clp_argv->argv = (char **)realloc(clp_argv->argv, sizeof(char *) * argcap)))
		goto failed_argv;
	}
	clp_argv->argv[clp_argv->argc++] = arg;
    }
    clp_argv->argv[clp_argv->argc] = 0;	/* know we have space */
    
    return clp_argv;

  failed_argv:
    free(clp_argv->argv_buf);
  failed_argv_buf:
    free(clp_argv);
  failed_all:
    return 0;
}

void
Clp_DeleteArgv(Clp_Argv *clp_argv)
{
    if (!clp_argv)
	return;
    free(clp_argv->argv);
    free(clp_argv->argv_buf);
    free(clp_argv);
}


/******
 * Clp_ParserStates
 **/

Clp_ParserState *
Clp_NewParserState(void)
{
    return (Clp_ParserState *)malloc(sizeof(Clp_ParserState));
}

void
Clp_DeleteParserState(Clp_ParserState *save)
{
    free(save);
}


void
Clp_SaveParser(Clp_Parser *clp, Clp_ParserState *save)
     /* Saves parser position in save */
{
    Clp_Internal *cli = clp->internal;
    save->argv = cli->argv;
    save->argc = cli->argc;
    memcpy(save->option_chars, cli->option_chars, 3);
    save->text = cli->text;
    save->is_short = cli->is_short;
    save->whole_negated = cli->whole_negated;
}


void
Clp_RestoreParser(Clp_Parser *clp, Clp_ParserState *save)
     /* Restores parser position from save */
{
    Clp_Internal *cli = clp->internal;
    cli->argv = save->argv;
    cli->argc = save->argc;
    memcpy(cli->option_chars, save->option_chars, 3);
    cli->text = save->text;
    cli->is_short = save->is_short;
    cli->whole_negated = save->whole_negated;
}


/*******
 * Clp_Next and its helpers
 **/

static void
set_option_text(Clp_Internal *cli, const char *text, int n_option_chars)
{
    char *option_chars = cli->option_chars;
    assert(n_option_chars < 3);

    while (n_option_chars-- > 0)
	*option_chars++ = *text++;
    *option_chars = 0;

    cli->text = text;
}


static int
next_argument(Clp_Parser *clp, int want_argument)
     /* Moves clp to the next argument.
	Returns 1 if it finds another option.
	Returns 0 if there aren't any more arguments.
	Returns 0, sets clp->have_arg = 1, and sets clp->arg to the argument
	if the next argument isn't an option.
	If want_argument > 0, it'll look for an argument.
	want_argument == 1: Accept arguments that start with Clp_NotOption
		or Clp_LongImplicit.
	want_argument == 2: Accept ALL arguments.
	
	Where is the option stored when this returns?
	Well, cli->argv[0] holds the whole of the next command line argument.
	cli->option_chars holds a string: what characters began the option?
	It is generally "-" or "--".
	cli->text holds the text of the option:
	for short options, cli->text[0] is the relevant character;
	for long options, cli->text holds the rest of the option. */
{
    Clp_Internal *cli = clp->internal;
    const char *text;
    int option_class;

    /* clear relevant flags */
    clp->have_arg = 0;
    clp->arg = 0;
    cli->could_be_short = 0;
  
    /* if we're in a string of short options, move up one char in the string */
    if (cli->is_short) {
	++cli->text;
	if (cli->text[0] == 0)
	    cli->is_short = 0;
	else if (want_argument > 0) {
	    /* handle -O[=]argument case */
	    clp->have_arg = 1;
	    if (cli->text[0] == '=')
		clp->arg = cli->text + 1;
	    else
		clp->arg = cli->text;
	    cli->is_short = 0;
	    return 0;
	}
    }
  
    /* if in short options, we're all set */
    if (cli->is_short)
	return 1;
  
    /** if not in short options, move to the next argument **/
    cli->whole_negated = 0;
    cli->text = 0;
  
    if (cli->argc <= 1)
	return 0;
  
    cli->argc--;
    cli->argv++;
    text = cli->argv[0];
  
    if (want_argument > 1)
	goto not_option;
  
    option_class = cli->option_class[(unsigned char)text[0]];
    if (text[0] == '-' && text[1] == '-')
	option_class = Clp_DoubledLong;
  
    /* If this character could introduce either a short or a long option,
       try a long option first, but remember that short's a possibility for
       later. */
    if ((option_class & (Clp_Short | Clp_ShortNegated))
	&& (option_class & (Clp_Long | Clp_LongNegated))) {
	option_class &= ~(Clp_Short | Clp_ShortNegated);
	if (text[1] != 0)
	    cli->could_be_short = 1;
    }
  
    switch (option_class) {
    
      case Clp_Short:
	cli->is_short = 1;
	goto check_singleton;
    
      case Clp_ShortNegated:
	cli->is_short = 1;
	cli->whole_negated = 1;
	goto check_singleton;
    
      case Clp_Long:
	goto check_singleton;
    
      case Clp_LongNegated:
	cli->whole_negated = 1;
	goto check_singleton;
    
      check_singleton:
	/* For options introduced with one character, option-char,
	   '[option-char]' alone is NOT an option. */
	if (text[1] == 0)
	    goto not_option;
	set_option_text(cli, text, 1);
	break;
    
      case Clp_LongImplicit:
	/* LongImplict: option_chars == "" (since all chars are part of the
	   option); restore head -> text of option */
	if (want_argument > 0)
	    goto not_option;
	set_option_text(cli, text, 0);
	break;
    
      case Clp_DoubledLong:
	set_option_text(cli, text, 2);
	break;
    
      not_option: 
      case Clp_NotOption:
	cli->is_short = 0;
	clp->have_arg = 1;
	clp->arg = text;
	return 0;
    
      default:
	assert(0 /* CLP misconfiguration: bad option type */);
    
    }
  
    return 1;
}


static void
switch_to_short_argument(Clp_Parser *clp)
{
    Clp_Internal *cli = clp->internal;
    const char *text = cli->argv[0];
    int option_class = cli->option_class[(unsigned char)text[0]];
    cli->is_short = 1;
    cli->whole_negated = (option_class & Clp_ShortNegated ? 1 : 0);
    set_option_text(cli, cli->argv[0], 1);
    assert(cli->could_be_short);
}


static Clp_Option *
find_long(Clp_Parser *clp, const char *arg)
     /* If arg corresponds to one of clp's options, finds that option &
	returns it. If any argument is given after an = sign in arg, sets
	clp->have_arg = 1 and clp->arg to that argument. Sets cli->ambiguous
	to 1 iff there was no match because the argument was ambiguous. */
{
    Clp_Internal *cli = clp->internal;
    int value, len, min_match;
    Clp_Option *opt = cli->opt;
    int first_negative_ambiguous;
  
    /* Look for a normal option. */
    value = find_prefix_opt
	(arg, cli->nopt, opt, cli->long_min_match,
	 &cli->ambiguous, cli->ambiguous_values, clp->negated);
    if (value >= 0)
	goto worked;
  
    /* If we can't find it, look for a negated option. */
    /* I know this is silly, but it makes me happy to accept
       --no-no-option as a double negative synonym for --option. :) */
    first_negative_ambiguous = cli->ambiguous;
    while (arg[0] == 'n' && arg[1] == 'o' && arg[2] == '-') {
	arg += 3;
	clp->negated = !clp->negated;
	value = find_prefix_opt
	    (arg, cli->nopt, opt, cli->long_min_match,
	     &cli->ambiguous, cli->ambiguous_values, clp->negated);
	if (value >= 0)
	    goto worked;
    }
  
    /* No valid option was found; return 0. Mark the ambiguous values found
       through '--no' by making them negative. */
    {
	int i, max = cli->ambiguous;
	if (max > MAX_AMBIGUOUS_VALUES) max = MAX_AMBIGUOUS_VALUES;
	for (i = first_negative_ambiguous; i < max; i++)
	    cli->ambiguous_values[i] = -cli->ambiguous_values[i] - 1;
    }
    return 0;
  
  worked:
    min_match = (clp->negated ? cli->long_min_match[value].neg : cli->long_min_match[value].pos);
    len = argcmp(opt[value].long_name, arg, min_match, 1); /* XXX 1? */
    assert(len > 0);
    if (arg[len] == '=') {
	clp->have_arg = 1;
	clp->arg = arg + len + 1;
    }
    return &opt[value];
}


static Clp_Option *
find_short(Clp_Parser *clp, int short_name)
     /* If short_name corresponds to one of clp's options, returns it. */
{
    Clp_Internal *cli = clp->internal;
    Clp_Option *opt = cli->opt;
    int i;
  
    for (i = 0; i < cli->nopt; i++)
	if (opt[i].short_name == short_name
	    && (clp->negated
		? TEST(&opt[i], Clp_Negate)
		: !TEST(&opt[i], Clp_OnlyNegated)))
	    return &opt[i];
  
    return 0;
}


int
Clp_Next(Clp_Parser *clp)
     /* Gets and parses the next argument from the argument list.
	
        If there are no more arguments, returns Clp_Done.
	If the next argument isn't an option, returns Clp_NotOption;
	the argument is stored in clp->arg.
	If the next argument is an option, returns that option's option_id.
	
	If the next argument is an unrecognizable or ambiguous option,
	an error message is given and Clp_BadOption is returned.
	
	If an option has an argument, that argument is stored in clp->arg
	and clp->have_arg is set to 1.
	Furthermore, that argument's parsed value (according to its type)
	is stored in the clp->val union.
	
	If an option needs an argument but isn't given one;
	if it doesn't need an argument but IS given one;
	or if the argument is the wrong type,
	an error message is given and Clp_BadOption is returned. */
{
    Clp_Internal *cli = clp->internal;
    Clp_Option *opt;
    Clp_ParserState clpsave;
    int complain;
  
    /** Set up clp **/
    cli->current_option = 0;
    cli->ambiguous = 0;
  
    /** Get the next argument or option **/
    if (!next_argument(clp, cli->option_processing ? 0 : 2))
	return clp->have_arg ? Clp_NotOption : Clp_Done;
  
    clp->negated = cli->whole_negated;
    if (cli->is_short)
	opt = find_short(clp, cli->text[0]);
    else
	opt = find_long(clp, cli->text);
  
    /** If there's ambiguity between long & short options, and we couldn't
	find a long option, look for a short option **/
    if (!opt && cli->could_be_short) {
	switch_to_short_argument(clp);
	opt = find_short(clp, cli->text[0]);
    }
  
    /** If we didn't find an option... **/
    if (!opt || (clp->negated && !TEST(opt, Clp_Negate))) {
    
	/* default processing for the "--" option: turn off option processing
	   and return the next argument */
	if (strcmp(cli->argv[0], "--") == 0) {
	    Clp_SetOptionProcessing(clp, 0);
	    return Clp_Next(clp);
	}
    
	/* otherwise, report some error or other */
	if (cli->ambiguous)
	    ambiguity_error(clp, cli->ambiguous, cli->ambiguous_values,
			    cli->opt, cli->option_chars,
			    "option '%s%s' is ambiguous",
			    cli->option_chars, cli->text);
	else if (cli->is_short && !cli->could_be_short)
	    Clp_OptionError(clp, "unrecognized option '%s%c'",
			    cli->option_chars, cli->text[0]);
	else
	    Clp_OptionError(clp, "unrecognized option '%s%s'",
			    cli->option_chars, cli->text);
	return Clp_BadOption;
    }
  
    /** Set the current option **/
    cli->current_option = opt;
    cli->current_short = cli->is_short;
    cli->negated_by_no = clp->negated && !cli->whole_negated;
  
    /** The no-argument (or should-have-no-argument) case **/
    if (clp->negated || !TEST(opt, Clp_AnyArgument)) {
	if (clp->have_arg) {
	    Clp_OptionError(clp, "'%O' can't take an argument");
	    return Clp_BadOption;
	} else
	    return opt->option_id;
    }
  
    /** Get an argument if we need one, or if it's optional **/
    /* Sanity-check the argument type. */
    if (opt->arg_type <= 0 || opt->arg_type >= cli->nargtype
	|| cli->argtype[ opt->arg_type ].func == 0)
	return Clp_Error;
  
    /* complain == 1 only if the argument was explicitly given,
       or it is mandatory. */
    complain = (clp->have_arg != 0) || TEST(opt, Clp_Mandatory);
    Clp_SaveParser(clp, &clpsave);
  
    if (TEST(opt, Clp_Mandatory) && !clp->have_arg) {
	/* Mandatory argument case */
	/* Allow arguments to options to start with a dash, but only if the
	   argument type allows it by not setting Clp_DisallowOptions */
	int disallow = TEST(&cli->argtype[opt->arg_type], Clp_DisallowOptions);
	next_argument(clp, disallow ? 1 : 2);
	if (!clp->have_arg) {
	    int got_option = cli->text != 0;
	    Clp_RestoreParser(clp, &clpsave);
	    if (got_option)
		Clp_OptionError(clp, "'%O' requires a non-option argument");
	    else
		Clp_OptionError(clp, "'%O' requires an argument");
	    return Clp_BadOption;
	}
    
    } else if (cli->is_short && !clp->have_arg && cli->text[1] != 0)
	/* The -[option]argument case:
	   Assume that the rest of the current string is the argument. */
	next_argument(clp, 1);
  
    /** Parse the argument **/
    if (clp->have_arg) {
	Clp_ArgType *atr = &cli->argtype[ opt->arg_type ];
	if (atr->func(clp, clp->arg, complain, atr->thunk) <= 0) {
	    /* parser failed */
	    clp->have_arg = 0;
	    if (TEST(opt, Clp_Mandatory))
		return Clp_BadOption;
	    else
		Clp_RestoreParser(clp, &clpsave);
	}
    }
  
    return opt->option_id;
}


const char *
Clp_Shift(Clp_Parser *clp, int allow_dashes)
     /* Returns the next argument from the argument list without parsing it.
        If there are no more arguments, returns 0. */
{
    Clp_ParserState clpsave;
    Clp_SaveParser(clp, &clpsave);
    next_argument(clp, allow_dashes ? 2 : 1);
    if (!clp->have_arg)
	Clp_RestoreParser(clp, &clpsave);
    return clp->arg;
}


/*******
 * Clp_OptionError
 **/

typedef struct Clp_BuildString {
    char *text;
    char *pos;
    int capacity;
    int bad;
} Clp_BuildString;

static Clp_BuildString *
new_build_string(void)
{
    Clp_BuildString *bs = (Clp_BuildString *)malloc(sizeof(Clp_BuildString));
    if (!bs) goto bad;
    bs->text = (char *)malloc(256);
    if (!bs->text) goto bad;
    bs->pos = bs->text;
    bs->capacity = 256;
    bs->bad = 0;
    return bs;

  bad:
    if (bs) free(bs);
    return 0;
}

static void
free_build_string(Clp_BuildString *bs)
{
    if (bs) free(bs->text);
    free(bs);
}

static int
grow_build_string(Clp_BuildString *bs, int want)
{
    char *new_text;
    int ipos = bs->pos - bs->text;
    int new_capacity = bs->capacity;
    while (want >= new_capacity)
	new_capacity *= 2;
    new_text = (char *)realloc(bs->text, new_capacity);
    if (!new_text) {
	bs->bad = 1;
	return 0;
    } else {
	bs->text = new_text;
	bs->pos = bs->text + ipos;
	bs->capacity = new_capacity;
	return 1;
    }
}

#define ENSURE_BUILD_STRING(bs, space) \
  ((((bs)->pos - (bs)->text) + (space) >= (bs)->capacity)		\
   || grow_build_string((bs), ((bs)->pos - (bs)->text) + (space)))

static void
append_build_string(Clp_BuildString *bs, const char *s, int l)
{
    if (l < 0)
	l = strlen(s);
    if (ENSURE_BUILD_STRING(bs, l)) {
	memcpy(bs->pos, s, l);
	bs->pos += l;
    }
}


static Clp_BuildString *
Clp_VaOptionError(Clp_Parser *clp, Clp_BuildString *bs,
		  const char *fmt, va_list val)
     /* Reports an error for parser clp. Allowable % format characters are:
	
	s	Print a string from the argument list.
	c	Print an int from the argument list as a character.
	d	Print an int from the argument list.
	O	Print the name of the current option;
		take nothing from the argument list.
		
	No field specifications or flags are allowed. Always returns 0. */
{
    Clp_Internal *cli = clp->internal;
    const char *percent;
  
    if (!bs)
	bs = new_build_string();
    if (!bs)
	return 0;
    append_build_string(bs, cli->program_name, -1);
    append_build_string(bs, ": ", 2);
  
    for (percent = strchr(fmt, '%'); percent; percent = strchr(fmt, '%')) {
	append_build_string(bs, fmt, percent - fmt);
	switch (*++percent) {
      
	  case 's': {
	      char *s = va_arg(val, char *);
	      if (s) append_build_string(bs, s, -1);
	      else append_build_string(bs, "(null)", 6);
	      break;
	  }
     
	  case 'c': {
	      int c = va_arg(val, int);
	      if (ENSURE_BUILD_STRING(bs, 4)) {
		  if (c >= 32 && c <= 126)
		      *bs->pos++ = c;
		  else if (c < 32) {
		      *bs->pos++ = '^';
		      *bs->pos++ = c + 64;
		  } else {
		      sprintf(bs->pos, "\\%03o", c);
		      bs->pos += 4;
		  }
	      }
	      break;
	  }
     
	  case 'd': {
	      int d = va_arg(val, int);
	      if (ENSURE_BUILD_STRING(bs, 32)) {
		  sprintf(bs->pos, "%d", d);
		  bs->pos = strchr(bs->pos, 0);
	      }
	      break;
	  }
     
	  case 'O': {
	      Clp_Option *opt = cli->current_option;
	      if (!opt)
		  append_build_string(bs, "(no current option!)", -1);
	      else if (cli->current_short) {
		  append_build_string(bs, cli->option_chars, -1);
		  if (ENSURE_BUILD_STRING(bs, 1))
		      *bs->pos++ = opt->short_name;
	      } else if (cli->negated_by_no) {
		  append_build_string(bs, cli->option_chars, -1);
		  append_build_string(bs, "no-", 3);
		  append_build_string(bs, opt->long_name, -1);
	      } else {
		  append_build_string(bs, cli->option_chars, -1);
		  append_build_string(bs, opt->long_name, -1);
	      }
	      break;
	  }
     
	  case '%':
	    if (ENSURE_BUILD_STRING(bs, 1))
		*bs->pos++ = '%';
	    break;
      
	  default:
	    if (ENSURE_BUILD_STRING(bs, 2)) {
		*bs->pos++ = '%';
		*bs->pos++ = *percent;
	    }
	    break;
	    
	}
	fmt = ++percent;
    }
  
    append_build_string(bs, fmt, -1);
    append_build_string(bs, "\n", 1);
  
    return bs;
}

static void
do_error(Clp_Parser *clp, Clp_BuildString *bs)
{
    const char *text;
    if (bs && !bs->bad) {
	*bs->pos = 0;
	text = bs->text;
    } else
	text = "out of memory\n";

    if (clp->internal->error_handler != 0)
	(*clp->internal->error_handler)(text);
    else
	fputs(text, stderr);
}

int
Clp_OptionError(Clp_Parser *clp, const char *fmt, ...)
{
    Clp_BuildString *bs;
    va_list val;
    va_start(val, fmt);
    bs = Clp_VaOptionError(clp, 0, fmt, val);
    va_end(val);
    do_error(clp, bs);
    free_build_string(bs);
    return 0;
}

static int
ambiguity_error(Clp_Parser *clp, int ambiguous, int *ambiguous_values,
		Clp_Option *opt, const char *prefix,
		const char *fmt, ...)
{
    Clp_BuildString *bs;
    int i;
    va_list val;
    va_start(val, fmt);
    bs = Clp_VaOptionError(clp, 0, fmt, val);
    if (!bs) goto done;

    append_build_string(bs, clp->internal->program_name, -1);
    append_build_string(bs, ": (Possibilities are", -1);

    for (i = 0; i < ambiguous && i < MAX_AMBIGUOUS_VALUES; i++) {
	int value = ambiguous_values[i];
	const char *no_dash = "";
	if (value < 0)
	    value = -(value + 1), no_dash = "no-";
	if (i == 0)
	    append_build_string(bs, " ", 1);
	else if (i == ambiguous - 1)
	    append_build_string(bs, (i == 1 ? " and " : ", and "), -1);
	else
	    append_build_string(bs, ", ", 2);
	append_build_string(bs, prefix, -1);
	append_build_string(bs, no_dash, -1);
	append_build_string(bs, opt[value].long_name, -1);
    }

    if (ambiguous > MAX_AMBIGUOUS_VALUES)
	append_build_string(bs, ", and others", -1);
    append_build_string(bs, ".)\n", -1);
    va_end(val);

  done:
    do_error(clp, bs);
    free_build_string(bs);
    return 0;
}

static int
copy_string(char *buf, int buflen, int bufpos, const char *what)
{
    int l = strlen(what);
    if (l > buflen - bufpos - 1)
	l = buflen - bufpos - 1;
    memcpy(buf + bufpos, what, l);
    return l;
}

int
Clp_CurOptionNameBuf(Clp_Parser *clp, char *buf, int buflen)
{
    Clp_Internal *cli = clp->internal;
    Clp_Option *opt = cli->current_option;
    int pos = 0;
    if (!opt)
	pos += copy_string(buf, buflen, pos, "(no current option!)");
    else if (cli->current_short) {
	pos += copy_string(buf, buflen, pos, cli->option_chars);
	if (pos < buflen - 1)
	    buf[pos++] = opt->short_name;
    } else if (cli->negated_by_no) {
	pos += copy_string(buf, buflen, pos, cli->option_chars);
	pos += copy_string(buf, buflen, pos, "no-");
	pos += copy_string(buf, buflen, pos, opt->long_name);
    } else {
	pos += copy_string(buf, buflen, pos, cli->option_chars);
	pos += copy_string(buf, buflen, pos, opt->long_name);
    }
    buf[pos] = 0;
    return pos;
}

const char *
Clp_CurOptionName(Clp_Parser *clp)
{
    static char buf[256];
    Clp_CurOptionNameBuf(clp, buf, 256);
    return buf;
}

#ifdef __cplusplus
}
#endif
